package com.ck.jdbc.util;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;

import com.mysql.jdbc.Driver;

/**
 * 
 * @author CK
 *
 */
public class MyPoolImpl implements IMyPool {

	/**
	 * 连接池配置参数，承接外部配置
	 */
	// 配置的文件
	private static String propertyFile = "jdbcData.properties";
	// 驱动的类路径
	private static String jdbcDriver = "";
	// 连接字符串
	private static String jdbcUrl = "";
	// 用户名
	private static String userName = "";
	// 密码
	private static String password = "";
	
	// 规范连接池管道数量参数
	// 连接池启动时的初始值
	private static int initialSize;
	
	// 增量，每次增加的值
	private static int stepSize;
	// 连接池的最大值
	private static int maxActive;
	
	// 最大空闲值.当经过一个高峰时间后，连接池可以慢慢将已经用不到的连接慢慢释放一部分，一直减少到maxIdle为止
	private static int maxIdle;

	// 最小空闲值.当空闲的连接数少于该值时，连接池就会预申请一些连接，以避免洪峰来时再申请而造成的性能开销
	private static int minIdle;
	// 最小空闲值.当空闲的连接数少于该值时，连接池就会预申请一些连接，以避免洪峰来时再申请而造成的性能开销
	private static int maxWait;
	
	// 放入管道对象的集合（最好是线程安全的）
	private static Vector<PooledConnection> pooledConnections = new Vector<PooledConnection>();
	
	/**
	 * 构造方法
	 */
	public MyPoolImpl() {
		// 初始化
		init();
	}
	
	private void init() {
		InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(propertyFile);
		Properties properties = new Properties();
		try {
			properties.load(inputStream);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// 驱动的类路径
		jdbcDriver = properties.getProperty("jdbcDriver");
		// 连接字符串
		jdbcUrl = properties.getProperty("jdbcUrl");
		// 用户名
		userName = properties.getProperty("userName");
		// 密码
		password = properties.getProperty("password");
		
		// 规范连接池管道数量参数
		// 连接池启动时的初始值
		initialSize = Integer.valueOf(properties.getProperty("initialSize"));
		
		// 增量，每次增加的值
		stepSize = Integer.valueOf(properties.getProperty("stepSize"));
		// 连接池的最大值
		maxActive = Integer.valueOf(properties.getProperty("maxActive"));
		
		// 最大空闲值.当经过一个高峰时间后，连接池可以慢慢将已经用不到的连接慢慢释放一部分，一直减少到maxIdle为止
		maxIdle = Integer.valueOf(properties.getProperty("maxIdle"));

		// 最小空闲值.当空闲的连接数少于该值时，连接池就会预申请一些连接，以避免洪峰来时再申请而造成的性能开销
		minIdle = Integer.valueOf(properties.getProperty("minIdle"));
		// 最小空闲值.当空闲的连接数少于该值时，连接池就会预申请一些连接，以避免洪峰来时再申请而造成的性能开销
		maxWait = Integer.valueOf(properties.getProperty("maxWait"));
		
		try {
			// mysql驱动
			Driver driver = (Driver) Class.forName(jdbcDriver).newInstance();
		
			// 启动注册
			DriverManager.registerDriver(driver);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// 对内创建管道
		createConnection(initialSize);
	}

	@Override
	public PooledConnection getConnection() {
		
		// 判断
		if(pooledConnections.size() == 0) {
			// TODO 日志打印
			System.out.println("连接池内没有管道对象！");
			throw new RuntimeException("连接池内没有管道对象！");
					
		}
		
		// 获取管道之前先检查管道在给别人
		PooledConnection realConnection = getRealConnection();
		
		// 当管道为空时，则创建新增的管道
		while(realConnection == null) {
		//	if(realConnection == null) {
			createConnection(stepSize);
			realConnection = getRealConnection(); // 线程在这里死了怎么办  活了
			
			// 睡眠一下，确保返回的realConnection是一个真正实例
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return realConnection;
	}

	/**
	 * 对外提供管道之前，先自我检查管道的有效性  synchronized处理
	 * @return
	 */
	private synchronized PooledConnection getRealConnection() {
		for (PooledConnection conn : pooledConnections) {
			
			// 如果管道不是繁忙的
			if(!conn.isBusy()) {
				Connection connection = conn.getConnection();
				
				// 判断管道在限定时间内是否是有效的
				try {
					if(!connection.isValid(maxWait)) {
						// 创建真正的物理连接管道
						Connection validConnection = DriverManager.getConnection(jdbcUrl, userName, password);
						conn.setConnection(validConnection);
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
				conn.setBusy(true);
				return conn;
			}
		}
		return null;
	}

	@Override
	public void createConnection(int count) {

		// 判断连接池最大值大于0，且连接池管道对象+当前创建的管道大于maxActive
		if(maxActive > 0 && (pooledConnections.size() + count) > maxActive) {
			// TODO 日志打印
			System.out.println("创建管道失败，连接池将超过最大上限！");
			throw new RuntimeException("创建管道失败，连接池将超过最大上限！");
		}
		
		for(int i = 0; i < count; i++) {
			try {
				// 创建真正的物理连接管道
				Connection connection = DriverManager.getConnection(jdbcUrl, userName, password);
				// 设置到自定义的连接管道
				PooledConnection pooledConnection = new PooledConnection(connection, false);
				
				// 加入到事先准备好的缓存当中(注意：已是线程安全的)
				pooledConnections.add(pooledConnection);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}
